package chapter1.zyq2_senFile;

import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;

public class SendReceiveUtil_File5 {

    //发送一个字符串
    public static void sendString(Socket socket, String msg) throws Exception{
        byte[] obytes = msg.getBytes("utf-8");
        send(socket, obytes);
    }

    //发送包含字节个数的一组字节。
    private static void send(Socket socket, byte[] obytes) throws Exception{
        OutputStream outs = socket.getOutputStream();
        int bytesCount = obytes.length;
        //先发送字节数
        byte[] cntBytes = intToByteArray(bytesCount);
        outs.write(cntBytes, 0, cntBytes.length);
        outs.flush();
        outs.write(obytes, 0, obytes.length);
        outs.flush();
    }


    //把整数转成对应的字节数组
    public static byte[] intToByteArray(int a) {
        return new byte[] {
                (byte) ((a >> 24) & 0xFF),
                (byte) ((a >> 16) & 0xFF),
                (byte) ((a >> 8) & 0xFF),
                (byte) (a & 0xFF)
        };
    }



    public static String receiveString(Socket socket) throws Exception{
        byte[] ibytes = recv(socket);
        String msg = new String(ibytes,0, ibytes.length,"utf-8");
        return msg;
    }

    //接收包含字节个数的一组字节。
    private static byte[] recv(Socket socket) throws Exception{
        byte[] countBytes = new byte[4];
        readFully(socket, countBytes);
        int count = byteArrayToInt(countBytes);
        byte[] dataBytes = new byte[count];
        readFully(socket, dataBytes);
        return dataBytes;
    }

    //从socket接收字节，把数组bytes填满
    private static void readFully(Socket socket, byte[] bytes) throws Exception{
        InputStream ins = socket.getInputStream();
        int bytesToRead = bytes.length;
        int readCount = 0;
        while (readCount < bytesToRead) {
            int result = ins.read(bytes, readCount, bytesToRead - readCount);
            // Stream意外结束
            if (result == -1) {
                throw new Exception("异常：InputStream意外结束!");
            }
            readCount += result;
        }
    }

    //把字节数组转成对应的整数
    public static int byteArrayToInt(byte[] b) {
        return b[3] & 0xFF |
                (b[2] & 0xFF) << 8 |
                (b[1] & 0xFF) << 16 |
                (b[0] & 0xFF) << 24;
    }





    public static byte[] longToByteArray(long fileLength){
        ByteBuffer buffer=ByteBuffer.allocate(8);//创建一个包含8个字节的缓冲区
        buffer.putLong(0,fileLength);
        return buffer.array();
    }

    public static void sendFile(Socket socket, String filePath) throws Exception {
        OutputStream os=socket.getOutputStream();
        File file=new File(filePath);
        long fileLength=file.length();
        byte[] lengthBytes=longToByteArray(fileLength);
        os.write(lengthBytes);
        os.flush();

        FileInputStream fis=new FileInputStream(file);
        byte[] buffer=new byte[1000*1000];
        int readLength=0;
        while( (readLength=fis.read(buffer))!=-1){
            os.write(buffer, 0, readLength);
            os.flush();
        }
        fis.close();
    }



    private static long byteArrayToLong(byte[] bytes) {
        //构建一个缓冲区大小为8字节的ByteBuffer流(刚创建的ByteBuffer处于写模式<等待放入数据>)
        ByteBuffer buffer=ByteBuffer.allocate(8);
        buffer.put(bytes,0 , bytes.length);//向ByteBuffer缓冲区放入byte[] byte数组的数据
        //调用缓冲区的flip()，使得其可读取(从写模式转化为读模式)
        //(方法的作用是：设置缓冲区不能设置位置，并将操作的起始位置设置为0，将操作的终止位置limit设置到数据的末尾位置)
        buffer.flip();
        return buffer.getLong();//从缓冲区获取long数据
    }

    public static void receiveFile(Socket socket, String filePath) throws Exception {
        //InputStream is=socket.getInputStream();

        byte[] lengthBytes=new byte[8];//用于存储一个8字节的long整数，表示文件长度
        readFully(socket,lengthBytes);//用socket的输入流读取数据存入到byte数组lengthBytes(读一个long数据表示文件长度)
        long restBytesToRead =byteArrayToLong(lengthBytes);//将一个表示文件长度的long数据，从byte数组转为long类型
        //restBytes：剩余要读取的字节数


        FileOutputStream fos=new FileOutputStream(new File(filePath));
        int bufferSize=1024*1024;
        byte[] dataBytes=new byte[bufferSize];
        do{
            if(bufferSize>restBytesToRead){//如果要缓冲区大小大于剩余字节长度
                bufferSize=(int)restBytesToRead;//就将剩余字节作为缓冲区大小重新构建缓冲区
                dataBytes=new byte[bufferSize];//重新构建缓冲区
            }
            readFully(socket,dataBytes);//用readFully将文件数据读取到缓冲区dataBytes中
            fos.write(dataBytes);//将缓冲区内容存入到电脑中的文件(读取的文件数据保存到电脑)
            restBytesToRead= restBytesToRead-bufferSize;//计算剩余尚未接收的字节数
        }while(restBytesToRead >0);
        fos.close();
    }

}
